package com.coupon.settlement.executor;

import com.coupon.common.constant.CouponCategoryEnum;
import com.coupon.common.exception.CouponException;
import com.coupon.common.vo.SettlementInfo;
import com.coupon.settlement.constant.RuleFlagEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 王哲
 * @Contact 1121586359@qq.com
 * @ClassName ExecutorManager.java
 * @create 2023年06月27日 下午3:39
 * @Description 优惠券结算规则执行管理器 根据用户的请求，找到对应的Executor去执行对应的结算规则
 * @Version V1.0
 */

/**
 * BeanPostProcessor 接口的作用是在Bean初始化前后进行一些处理操作
 */
@Component
@Slf4j
public class ExecutorManager implements BeanPostProcessor {

    // 规则执行器映射
    // key:结算规则类型 value:对应的结算规则执行器
    private static final Map<RuleFlagEnum, RuleExecutor> executorIndex =
            new HashMap<>(RuleFlagEnum.values().length);

    /**
     * 优惠券结算规则计算入口
     * 注意：一定要保证传递进来的优惠券个数 >= 1
     * @param settlementInfo
     * @return
     * @throws CouponException
     */
    public SettlementInfo computeRule(SettlementInfo settlementInfo)
            throws CouponException {
        SettlementInfo result = null;

        // 单类优惠券
        if (settlementInfo.getCouponAndTemplateInfos().size()==1){
            // 获取优惠券的类别
            CouponCategoryEnum categoryEnum = CouponCategoryEnum.of(
                    settlementInfo.getCouponAndTemplateInfos().get(0)
                            .getTemplateSDK().getCategory()
            );
            // 根据优惠券的类型获取对应的执行器
            switch (categoryEnum){
                case LIJIAN:
                    result = executorIndex.get(RuleFlagEnum.LIJIAN)
                            .computeRule(settlementInfo);
                    break;
                case MANJIAN:
                    result = executorIndex.get(RuleFlagEnum.MANJIAN)
                            .computeRule(settlementInfo);
                    break;
                case ZHEKOU:
                    result = executorIndex.get(RuleFlagEnum.ZHEKOU)
                            .computeRule(settlementInfo);
                    break;
            }
        }else {
            // 多类优惠券
            List<CouponCategoryEnum> categoryEnums = new ArrayList<>(
                    settlementInfo.getCouponAndTemplateInfos().size()
            );
            settlementInfo.getCouponAndTemplateInfos().forEach(ct->
                    categoryEnums.add(CouponCategoryEnum.of(
                            ct.getTemplateSDK().getCategory()
                    ))
            );

            if (categoryEnums.size()!=2){
                throw new CouponException("Not Support For More " +
                        "Template Category");
            }else {
                // 两类优惠券 满减+折扣
                if (categoryEnums.contains(CouponCategoryEnum.MANJIAN) &&
                categoryEnums.contains(CouponCategoryEnum.ZHEKOU)){
                    result = executorIndex.get(RuleFlagEnum.MANJIAN_ZHEKOU)
                            .computeRule(settlementInfo);
                }else {
                    throw new CouponException("Not Support For Other " +
                            "Template Category");
                }
            }
        }

        return result;
    }

    /**
     * Bean初始化前置处理器
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        if (!(bean instanceof RuleExecutor)) {
            return bean;
        }
        RuleExecutor executor = (RuleExecutor) bean;
        RuleFlagEnum ruleFlagEnum = executor.ruleConfig();
        if (executorIndex.containsKey(ruleFlagEnum)) {
            throw new IllegalStateException(
                    "There is already an executor for rule flag: " + ruleFlagEnum);
        }
        log.info("Load executor {} for rule flag {}.", executor.getClass(), ruleFlagEnum);
        executorIndex.put(ruleFlagEnum, executor);

        return bean;
    }

    /**
     * Bean初始化后置处理器
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }
}
